package handler

import (
	"context"
	"errors"

	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/emptypb"
	"gorm.io/gorm"

	"mxshop_srvs/order_srv/dao/mysql"
	"mxshop_srvs/order_srv/model"
	proto "mxshop_srvs/order_srv/proto/gen/v1"
)

func CartItemRecordToProto(record *mysql.CartItemRecord) *proto.CartItemSimpleEntity {
	return &proto.CartItemSimpleEntity{
		Id:      record.ID,
		UserId:  record.UserId,
		GoodsId: record.GoodsId,
		Nums:    record.Nums,
		Checked: record.Checked,
	}
}

// CartItemList 获取购物车商品列表
func (s *Service) CartItemList(_ context.Context, req *proto.CartItemListRequest) (*proto.CartItemListResponse, error) {
	count, err := s.DB.CartItemCount(req.UserId)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, status.Error(codes.NotFound, "")
		}
		return nil, status.Error(codes.Internal, "")
	}
	cartItems, err := s.DB.GetCartItems(req.UserId, false)
	if err != nil {
		return nil, status.Error(codes.Internal, "")
	}

	data := make([]*proto.CartItemSimpleEntity, 0, len(cartItems))
	for _, item := range cartItems {
		data = append(data, CartItemRecordToProto(item))
	}

	return &proto.CartItemListResponse{
		Total: int32(count),
		Data:  data,
	}, nil
}

// CreateCartItem 添加商品到购物车
func (s *Service) CreateCartItem(_ context.Context, req *proto.CartItem) (*proto.CartItemSimpleEntity, error) {
	cartItem, err := s.DB.GetCartItem(mysql.CartItem{
		UserID: req.UserId,
		GoodID: req.GoodsId,
	})
	if err != nil {
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, status.Error(codes.Internal, "")
		}
	}

	cartItem.Nums += req.Nums // 如果没有那Nums的默认零值是`0`,直接加即可。如果有那就在原有的数量上再加新数量。

	// 1. 购物车中原本没有这件商品 - 新建一个记录
	if cartItem.ID == 0 { // 如果没记录那ID默认零值为`0`
		cartItem.GoodsId = req.GoodsId
		cartItem.UserId = req.UserId
		cartItem.Checked = req.Checked
		cartItemRecord, err := s.DB.CreateCartItem(cartItem)
		if err != nil {
			return &proto.CartItemSimpleEntity{}, status.Error(codes.Internal, "")
		}
		cartItem = *cartItemRecord
	} else {
		// 2. 这个商品之前添加到了购物车 - 合并
		err := s.DB.UpdateCartItem(cartItem)
		if err != nil {
			return &proto.CartItemSimpleEntity{}, status.Error(codes.Internal, "")
		}
	}

	return CartItemRecordToProto(&cartItem), nil
}

// UpdateCartItem 更新购物车商品
func (s *Service) UpdateCartItem(_ context.Context, req *proto.CartItemSimpleEntity) (*emptypb.Empty, error) {
	cartItem, err := s.DB.GetCartItem(mysql.CartItem{
		ID:     req.Id,
		UserID: req.UserId,
		GoodID: req.GoodsId,
	})
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, status.Error(codes.NotFound, "购物车记录不存在")
		}
		return nil, status.Error(codes.Internal, "")
	}

	cartItem.Checked = req.Checked
	cartItem.Nums = req.Nums

	err = s.DB.UpdateCartItem(cartItem)
	if err != nil {
		return nil, status.Error(codes.Internal, "")
	}
	return &emptypb.Empty{}, nil
}

// DeleteCartItem 删除购物车商品
func (s *Service) DeleteCartItem(_ context.Context, req *proto.DeleteCartItemRequest) (*emptypb.Empty, error) {
	row, err := s.DB.DeleteCartItem(req)
	if err != nil {
		return nil, status.Error(codes.Internal, "")
	}
	if row == 0 {
		return nil, status.Error(codes.NotFound, "购物车记录不存在")
	}
	return &emptypb.Empty{}, nil
}

func OrderModelToProto(o *model.Order) *proto.OrderEntity {
	return &proto.OrderEntity{
		Id: o.ID,
		Order: &proto.Order{
			UserId:  o.UserID,
			OrderSn: o.OrderSn,
			PayType: proto.PayType(o.PayType),
			Status:  proto.OrderStatus(o.Status),
			Post:    o.Post,
			Total:   o.AmountCents,
			Address: o.Address,
			Name:    o.ConsigneeName,
			Mobile:  o.ConsigneeMobile,
			AddTime: o.CreatedAt.Format("2006-01-02 15:04:05"),
		},
	}
}

// OrderIDGenerator generates an id for the new order.
type OrderIDGenerator interface {
	GenerateInt64ID() int64
}

// CreateOrder 创建订单
func (s *Service) CreateOrder(_ context.Context, req *proto.CreateOrderRequest) (*proto.OrderEntity, error) {
	// 1. 从购物车中获取到选中的商品
	cartItems, err := s.DB.GetCartItems(req.UserId, true) // 获取购物车中选中的商品
	if err != nil {
		return nil, status.Error(codes.Internal, "")
	}
	var cartItemsCount = len(cartItems)
	if cartItemsCount == 0 {
		return nil, status.Error(codes.InvalidArgument, "没有选中结算的商品")
	}

	goodsIDs := make([]int32, 0, cartItemsCount) // 存放购物车每件商品的ID。用于访问商品服务时获取购买商品所需信息
	goodsNumsMap := make(map[int32]int32)        // 存放购物车每件商品的购买数量。通过该数量以及获取到的商品价格信息,计算出总金额
	for _, cartItem := range cartItems {
		goodsIDs = append(goodsIDs, cartItem.GoodsId)
		goodsNumsMap[cartItem.GoodsId] = cartItem.Nums
	}

	// 2. 商品的价格自己查询 - 访问商品服务 (跨微服务)
	// 3. 库存的扣减 - 访问库存服务 (跨微服务)
	orderAmount, orderGoods, err := s.OrderManager.OrderHandling(context.Background(), goodsIDs, goodsNumsMap)
	if err != nil {
		return nil, status.Error(codes.FailedPrecondition, err.Error()) // 前置条件失败
	}

	// 4. 订单的基本信息表 - 订单的商品信息表
	orderSn := s.OrderIDGenerator.GenerateInt64ID() // 生成orderID
	var orderInfo = mysql.OrderInfo{
		UserID:          req.UserId,
		OrderSn:         orderSn,
		AmountCents:     orderAmount,
		Address:         req.Address,
		ConsigneeName:   req.Name,
		ConsigneeMobile: req.Mobile,
		Post:            req.Post,
	}
	// // 4.1 创建订单
	// orderID, err := s.DB.CreateOrder(orderInfo)
	// if err != nil {
	// 	return nil, status.Error(codes.Internal, "创建订单失败")
	// }
	// // 4.2 批量插入orderGoods
	// err = s.DB.BatchInsertOrderGoods(orderGoods, 100)
	// if err != nil {
	// 	return nil, status.Error(codes.Internal, "创建订单失败")
	// }
	//
	// // 5. 从购物车中删除已购买的记录
	// err = s.DB.DeleteCheckedCartItem(req.UserId)
	// if err != nil {
	// 	return nil, status.Error(codes.Internal, "创建订单失败")
	// }

	// 这里应该使用分布式事务,现在简单使用本地事务解决
	orderID, err := s.DB.CreateOrderByTrans(orderInfo, orderGoods, 100)
	if err != nil {
		return nil, status.Error(codes.Internal, "创建订单失败")
	}

	order, err := s.DB.GetOrder(orderID, 0)
	if err != nil {
		return nil, status.Error(codes.Internal, "创建订单失败")
	}
	return OrderModelToProto(&order), nil
}

// OrderList 获取订单列表
func (s *Service) OrderList(_ context.Context, req *proto.OrderFilterRequest) (*proto.OrderListResponse, error) {
	count, err := s.DB.UserOrderCount(req.UserId)
	if err != nil {
		return nil, status.Error(codes.Internal, "")
	}
	orders, err := s.DB.GetUserOrderList(req)
	if err != nil {
		return nil, status.Error(codes.Internal, "")
	}
	if len(orders) == 0 {
		return nil, status.Error(codes.NotFound, "")
	}

	orderEntityList := make([]*proto.OrderEntity, 0, len(orders))
	for _, order := range orders {
		orderEntityList = append(orderEntityList, OrderModelToProto(order))
	}

	return &proto.OrderListResponse{
		Total:  int32(count),
		Orders: orderEntityList,
	}, nil
}

func OrderGoodsModelToProto(og *model.OrderGoods) *proto.OrderItemEntity {
	return &proto.OrderItemEntity{
		Id:         og.ID,
		OrderId:    og.OrderID,
		GoodsId:    og.GoodsID,
		GoodsName:  og.GoodsName,
		GoodsImage: og.GoodsImage,
		GoodsPrice: og.GoodsPrice,
		Nums:       og.Nums,
	}
}

// OrderDetail 获取订单详情
func (s *Service) OrderDetail(_ context.Context, req *proto.OrderDetailRequest) (*proto.OrderDetailResponse, error) {
	// service层需要做得通用些:
	// 1.当web层是电商系统。那么需要校验当前查询的订单是否为该用户,因此在web层需要先去做一个当前用户与订单的校验。
	// 2.当web层是后台管理系统。只用传个OrderID即可,不需要校验是否是当前用户。
	order, err := s.DB.GetOrderDetail(req)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, status.Error(codes.NotFound, "订单不存在")
		}
		return nil, status.Error(codes.Internal, "")
	}

	// 订单的详情还需要携带商品信息快照(不需要再额外去查询每个商品当前的价格、图片等信息)
	orderGoods, err := s.DB.GetOrderGoods(order.ID)
	if err != nil {
		return nil, status.Error(codes.Internal, "")
	}

	goods := make([]*proto.OrderItemEntity, 0, len(orderGoods))
	for _, good := range orderGoods {
		goods = append(goods, OrderGoodsModelToProto(good))
	}

	return &proto.OrderDetailResponse{
		Order: OrderModelToProto(&order),
		Goods: goods,
	}, nil
}

// UpdateOrderStatus 更新订单状态
func (s *Service) UpdateOrderStatus(_ context.Context, req *proto.UpdateOrderStatusRequest) (*emptypb.Empty, error) {
	row, err := s.DB.UpdateOrderStatus(req)
	if err != nil {
		return nil, status.Error(codes.Internal, "")
	}
	if row == 0 {
		return nil, status.Error(codes.NotFound, "查询该订单失败")
	}
	return &emptypb.Empty{}, nil
}

// UpdateOrder 更新订单
func (s *Service) UpdateOrder(_ context.Context, req *proto.UpdateOrderRequest) (*emptypb.Empty, error) {
	row, err := s.DB.UpdateOrder(req)
	if err != nil {
		return nil, status.Error(codes.Internal, "")
	}
	if row == 0 {
		return nil, status.Error(codes.NotFound, "查询该订单失败")
	}
	return &emptypb.Empty{}, nil
}
